/*
xbObjects.js 2001-12-05

Contributor(s): Bob Clary, Original Work, Copyright 2000
                Bob Clary, Netscape Communications, Copyright 2001

Netscape grants you a royalty free license to use or modify this
software provided that this copyright notice appears on all copies.
This software is provided "AS IS," without a warranty of any kind.

*/

registerFile('xbObjects.js', 'base.js');

function _Classes()
{
  if (typeof(_classes) != 'undefined')
    reportError('Only one instance of _Classes() can be created');
    
  function registerClass(className, parentClassName)
  {
    if (!className)
      reportError(new xbException('registerClass: className not given', 'xbObjects.js', '_Classes::registerClass'));
      
    if (hasProperty(_classes, className))
      return;
      
    if (className != 'xbObject' && !parentClassName)
      parentClassName = 'xbObject';
      
    if (!parentClassName)
      parentClassName = null;
    else if ( !hasProperty(_classes, parentClassName))
      reportError(new xbException('registerClass: parentClassName ' + parentClassName + ' not defined', 'xbObjects.js', '_Classes::registerClass'));

    var objClass;
    objClass        = _classes[className] = new Object();
    // evaluating and caching the prototype object in registerClass
    // works so long as we are dealing with 'normal' source files
    // where functions are created in the global context and then 
    // statements executed. when evaling code blocks as in xbCOM,
    // this no longer works and we need to defer the prototype caching
    // to the defineClass method
    objClass.prototypeObject = null;
    objClass.parentClass  = parentClassName;
  }
  _Classes.prototype.registerClass = registerClass;

  function defineClass(className, prototype_func)
  {
    var p;

    if (!className)
      reportError( new xbException('defineClass: className not given', 'xbObjects.js', '_Classes::defineClass'));
      
    if (!hasProperty(_classes, className))
      reportError( new xbException('defineClass: className ' + className + ' not registered', 'xbObjects.js', '_Classes::defineClass'));
    
    if (hasProperty(_classes[className], 'prototype_called'))
      return;
      
    var classRef    = _classes[className];
    if (!classRef.prototypeObject)
      classRef.prototypeObject  = eval( className + '.prototype' );
    var childPrototype  = classRef.prototypeObject;
    var parentClassName = classRef.parentClass;
      
    if (parentClassName)
    {
      if ( !hasProperty(_classes, parentClassName) )
        reportError( new xbException('defineClass: parentClassName ' + parentClassName + ' not registered', 'xbObjects.js', '_Classes::defineClass'));

      var parentClassRef = _classes[parentClassName];
      
      if ( ! hasProperty(parentClassRef, 'prototype_called') )
      {
        // force parent's prototype to be called by creating a dummy instance
        // note constructor must handle 'default' constructor case
        var dummy;
        eval('dummy = new ' + parentClassName + '();');
      }
        
      var parentPrototype = parentClassRef.prototypeObject;
      //var childSuperClass = classRef.superClass;
    
      for (p in parentPrototype)
      {
        if (p != 'isa' && p != 'classRef' && p != 'parentMethod')
          childPrototype[p] = parentPrototype[p];
      }
    }

    prototype_func();
    
    childPrototype['isa']          = className;
    childPrototype['classRef']        = classRef;
    childPrototype['parentMethod']      = xbObject.prototype.parentMethod;
    
    _classes[className].prototype_called  = true;
  }
  _Classes.prototype.defineClass = defineClass;

}

// create global instance
var _classes = new _Classes();

    
// register root class xbObject
_classes.registerClass('xbObject');

function xbObject()
{
  _classes.defineClass('xbObject', _prototype_func);

  this.init();
  
  function _prototype_func()
  {
    // isa is set by defineClass() to the className
    // Note that this can change dynamically as the class is cast
    // into it's ancestors...
    xbObject.prototype.isa        = null;  
    
    // classref is set by defineClass() to point to the 
    // _classes entry for this class. This allows access 
    // the original _class's entry no matter how it has 
    // been recast. 
    // *** This will never change!!!! ***
    xbObject.prototype.classRef      = null;
    
    function init() { }
    xbObject.prototype.init        = init;
    
    function destroy() {}
    xbObject.prototype.destroy      = destroy;

    function parentMethod(method, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
    {
      var className  = this.isa;
      var parentClass  = _classes[className].parentClass;
      var tempMethod  = _classes[parentClass].prototypeObject[method];
      
      // find who implemented this method
      while (parentClass != 'xbObject' && tempMethod == this[method])
      {
        parentClass = _classes[parentClass].parentClass;
        tempMethod  = _classes[parentClass].prototypeObject[method];
      }
        
      this.tempMethod  = tempMethod;
      this.isa    = parentClass;
      var retVal    = this.tempMethod(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
      this.isa    = className;
      
      return retVal;
    }
    xbObject.prototype.parentMethod    = parentMethod;
  }
}

_classes.registerClass('xbException');

function xbException(errorMessage, errorFile, errorFunction, exception)
{
  _classes.defineClass('xbException', _prototype_func);
  
  this.init(errorMessage, errorFile, errorFunction, exception);
  
  function _prototype_func()
  {
    function init(errorMessage, errorFile, errorFunction, exception)
    {
      if (typeof(exception) == 'object' && typeof(exception.isa) != 'undefined' && exception.isa == 'xbException')
      {
        this.errorMessage  = exception.errorMessage;
        this.errorFile = exception.errorFile;
        this.errorFunction = exception.errorFunction;
        this.nativeMessage  = exception.nativeMessage;
        this.nativeException = exception.nativeException;
      }
      else
      {
        this.errorMessage  = errorMessage;
        this.errorFile = errorFile;
        this.errorFunction = errorFunction;
        this.nativeMessage  = '';

        if (typeof(exception) == 'object')
        {
          var p;
              
          for (p in exception)
            if (typeof(exception[p]) == 'string' || typeof(exception[p]) == 'number')
              this.nativeMessage += p + ' = ' + exception[p] + ' ';
              
          this.nativeException = exception;
        }
      }
    }
    xbException.prototype.init = init;
    
    function toString()
    {
      var s = 'xbException(' + this.errorFile + ' : ' + this.errorFunction + ') ' +
              this.errorMessage + ' / ' + this.nativeMessage;
      return s;
    }
    xbException.prototype.toString = toString;
  }
}
// eof: xbObjects.js
//</SCRIPT>
